/*
 * Created on May 4, 2005
 *
 * TODO To change the template for this generated file go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
package alloy.log;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.StringWriter;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;

import alloy.ast.Command;
import alloy.ast.Specification;
import alloy.util.Dbg;
import alloy.util.Params;
import alloy.util.ResourceManager;
import alloy.util.Util;

/**
 * @author xli15
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class AlloyLog {
	   /****************************************************************/
    /* The code that follows is used for keeping a log of performed analyses.
       Knowing how users work with the analyzer should help improving it. */
    /** top-level directory name for the log (null if no logging) */
    private static File _logDir = null;
    
    private static File _sessions[];
    private static int MAX_SESSION_LEVELS = 22;
    private static String _sessionPrefix[] = {"ui", /*"file",*/ "build", 
    	"command", "open", "new", "abort", "metamodel", "next", "reload", "unsat",
    	"saveFile", "saveFileAs", "saveCopyAs", "openReadOnly", "userGuide", "visualization",
    	"hideEditor", "hideOutput", "hideTables", "showEditor", "showOutput", "showTables"};

    //duplicate these so we can log visualizations
    //private int currentViewMode;
    private static final int VIZ_MODE = 0;
    private static final int TREE_MODE = 1;
    private static final int TEXT_MODE = 2;
    private static final int XML_MODE = 4;
    private static final int XML_ABBREV_MODE = 6;
    private static final int TABLE_MODE = 7;
    private static final int DOT_MODE = 5;
    private static final int CONSOLE_MODE = 3;
        
    //Log posting options
    private static final int askChangeMenu = 1;
    private static final int changeMenu = 2;
    private static final int askedToPostMenu = 3;
    
    //check if logging was never
    private static boolean wasNever = false;
    
    private static HashMap modelMap;
    
    private static long time_ui_created = 0;
    private static long time_last_build = 0;
    private static long time_last_command = 0;
    private static long time_last_open = 0;
    private static long time_last_new = 0;
    private static long time_last_open_read_only = 0;
    
    private static int last_session = 0;
    
    //For diff set up
    private static int buildNum = 0;
    private static int cfgNum = 0;
    private static int modelNum = 0;
    private static int specNum = 0;
    private static String buildF1;
    private static String buildF2;
    private static String specF1;
    private static String specF2;
    private static String cfg1;
    private static String cfg2;
    private static String diffHolder;
    
    public static final int UI = 0;
    //public static final int FILE = 1;
    public static final int BUILD = 1;
    public static final int COMMAND = 2;
    public static final int OPEN = 3;
    public static final int NEW = 4;
    public static final int ABORT = 5;
    public static final int METAMODEL = 6;
    public static final int NEXT = 7;
    public static final int RELOAD = 8;
    public static final int UNSAT = 9;
    public static final int SAVE_FILE = 10;
    public static final int SAVE_FILE_AS = 11;
    public static final int SAVE_COPY_AS = 12;
    public static final int OPEN_READ_ONLY = 13;
    public static final int USER_GUIDE = 14;
    public static final int VISUALIZATION = 15;
    public static final int HIDE_EDITOR = 16;
    public static final int HIDE_OUTPUT = 17;
    public static final int HIDE_TABLES = 18;
    public static final int SHOW_EDITOR = 19;
    public static final int SHOW_OUTPUT = 20;
    public static final int SHOW_TABLES = 21;
    
    public static final String LOG_DIR_PREFIX = "alloylog";
    
    private static final boolean ifAutoLog = true;
    private static final int autoLogLevel = BUILD;
    
    public static void setupLogDir()
    {
        final boolean logging = isLoggingOn();
        if (!logging) {
            _logDir = null;
            return;
        }
        
        if (_logDir != null) return;
        
        _logDir = new File(System.getProperty("java.io.tmpdir")+"");
        if (!(_logDir.isAbsolute() && _logDir.exists() && _logDir.isDirectory())) {
            // try to create the temporary directory under the Alloy program directory
            _logDir = new File("../tmp");
            if (!_logDir.exists())
            	_logDir.mkdir();
        }
        Dbg.chk(_logDir.exists() && _logDir.isDirectory(),
            "Could not find temporary directory " + _logDir.getAbsolutePath());
        
        File alloyLogSubdir = null;
        try {
            alloyLogSubdir = File.createTempFile(LOG_DIR_PREFIX, "", _logDir);
        }
        catch (IOException e) {
            Dbg.chk(false,
                "setUpLogDir: Log directory not writeable: " + alloyLogSubdir.getAbsolutePath());

        }
        Dbg.chk(alloyLogSubdir.exists() && alloyLogSubdir.isFile(),
            "setUpLogDir: Temporary directory not writeable: " + alloyLogSubdir.getAbsolutePath());
        alloyLogSubdir.delete();
        alloyLogSubdir.mkdir();

        Dbg.chk(alloyLogSubdir.exists() && alloyLogSubdir.isDirectory(),
            "setUpLogDir: Log directory not writeable: " + alloyLogSubdir.getAbsolutePath());
        _logDir = alloyLogSubdir.getAbsoluteFile();
        Dbg.info("Log directory: " + _logDir.getAbsolutePath());
        
        _sessions = new File[MAX_SESSION_LEVELS];
        for(int i = 0; i < MAX_SESSION_LEVELS; i++)
        	_sessions[i] = null;
    }
    
    private static void packLogs(File log) throws Exception
	{
        if (!isLoggingOn()) return;
        
		//create a ZipOutputStream to zip the data to "log"
		ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(log));

		File pdir = _logDir.getParentFile();
		
		String[] dirList = pdir.list(new AlloyLogWildCardFilter());
		int bytesIn = 0;
		//loop through dirList, and zip the files
		for (int i = 0; i < dirList.length; i++) {
			File f = new File(pdir, dirList[i]);
			
			if(f.isDirectory())
				zipDir(f, f, zos);
			//else if(f.isFile())
				//addZip(zos, new ZipFile(f));
		}
		
		zos.close();
	}
    
    //delete all log directories, including _logDir and legacy log directories.
    private static void deleteLogDirs(boolean ifDeleteSelf) {
        if (!isLoggingOn()) return;
		File pdir = _logDir.getParentFile();
		String[] dirList = pdir.list(new AlloyLogWildCardFilter());
		int bytesIn = 0;
		//loop through dirList, and zip the files
		for (int i = 0; i < dirList.length; i++) {
			File f = new File(pdir, dirList[i]);
			
			if(!f.isDirectory())
				continue;
			
			if(_logDir.getAbsoluteFile().compareTo(f.getAbsoluteFile()) == 0){
				if(ifDeleteSelf)
					deleteDir(f);
			} else
				deleteDir(f);
		}
        
		if(ifDeleteSelf){
			_logDir = null;
			for (int i = 0; i < MAX_SESSION_LEVELS; i++)
				_sessions[i] = null;
		}
    }
    
    private static boolean isLoggingOn() {
        // check if it's turned on
    	boolean logon = true;
    	String logging = System.getProperty("alloy.logging");
        if (logging == null)
            logging = Params.glob.getParam("DEVEL","logging");
        else
            Params.glob.setParam("DEVEL","logging", logging);

    	if (logging.equals("Always"))
    	    logon = true;
    	else if (logging.equals("Ask"))
    	    logon = true;
    	else if (logging.equals("Never"))
    	    logon = false;

        if (!logon) {
            _logDir = null;
            return false;
        } else {return true;}
    }

    private static boolean ifAskBeforePost() {
        // check if it's turned on
    	boolean ask = false;
    	String logging = System.getProperty("alloy.logging");
        if (logging == null)
            logging = Params.glob.getParam("DEVEL","logging");
    	if (logging.equals("Always"))
    	    ask = false;
    	else if (logging.equals("Ask"))
    	    ask = true;
    	else if (logging.equals("Never"))
    	    ask = false;

    	return ask;
    }
    
    /** return file content as string (null if there're problems) */
    private static String getContent(String filename_) {
        try {
            java.io.BufferedReader reader = new java.io.BufferedReader(new java.io.FileReader(filename_));
            String content = "";
            String line = reader.readLine();
            while (line != null) {
                content = content + line + "\n";
                line = reader.readLine();
            }
            return content;
        } catch (java.io.FileNotFoundException e) {
            return null;
        } catch (java.io.IOException e) {
            return null;
        }
    }
    
    public static void logEmailComment(String email, String comment){
    	saveLogFile(_sessions[UI],"EMAIL_COMMENT", email + "\n" + comment ); 	
    }
    
    /** save the models prepared for analysis */
    public static void logStartSession(int session) {
        if (!isLoggingOn()) return;
    	switch(session) {
    		case BUILD:
    			time_last_build = System.currentTimeMillis();
    			break;
    		case COMMAND:
    			time_last_command = System.currentTimeMillis();
    			break;
    		case OPEN:
    			time_last_open = System.currentTimeMillis();
    			break;
    		case NEW:
    			time_last_new = System.currentTimeMillis();
    			break;
    		case OPEN_READ_ONLY:
    			time_last_open_read_only = System.currentTimeMillis();
    			break;
    	}
    	
    	if(wasNever == true){	
            if (!isLoggingOn()) return;
            setupLogDir();
            File dir = new File(_logDir, _sessionPrefix[UI] + "-" + time_ui_created);
            dir.delete();
            dir.mkdir();
            if (!(dir.exists() && dir.isDirectory())) {
               Dbg.warn("Could not create logging directory: " + dir.getAbsolutePath());
               return;
            }
            _sessions[UI] = dir.getAbsoluteFile();
            
            if (session == COMMAND || session == METAMODEL) {
                File buildDir = new File(dir, _sessionPrefix[BUILD] + "-" + time_last_build);
                buildDir.delete();
                buildDir.mkdir();
                if (!(buildDir.exists() && buildDir.isDirectory())) {
                   Dbg.warn("Could not create logging directory: " + buildDir.getAbsolutePath());
                   return;
                }
                _sessions[BUILD] = buildDir.getAbsoluteFile();
                
                if (session == NEXT || session == VISUALIZATION || session == UNSAT) {
                    File command = new File(_sessions[COMMAND], _sessionPrefix[COMMAND] + "-" + time_last_command);
                    command.delete();
                    command.mkdir();
                    if (!(command.exists() && command.isDirectory())) {
                       Dbg.warn("Could not create logging directory: " + command.getAbsolutePath());
                       return;
                    }
                    _sessions[COMMAND] = command.getAbsoluteFile();	
                }
            }
            wasNever = false;
    	}
    	
    	if ((session == UI)  && !isLoggingOn()) {
            wasNever = true;
            time_ui_created = System.currentTimeMillis();
    	}
    	
        if (!isLoggingOn()) return;
        setupLogDir();
        
        File pdir;
        if(session > 0)
        	pdir = _sessions[session-1];
        else
        	pdir = _logDir;
        
        switch (session) {
        	case UI:
        		String scrollText = new String();
        		try {
                            int index;
        		    InputStream in = 
        			ResourceManager.getResourceAsStream("splash.txt");
        		    OutputStream out = new ByteArrayOutputStream();
        		    while(true){
        		    	int b = in.read();
        		    	if (b == -1) break;
        		    	out.write(b);
        		    }
                            scrollText = out.toString();
                            index = scrollText.indexOf("\n");
                            saveLogFile(pdir, "VERSION", scrollText.substring(0,index) + "\n");
        		    scrollText = out.toString();
        		} catch (IOException ex) {
        		    scrollText = "ERROR: could not read splash screen text from "
        			+ "splash.txt";
        		    Dbg.info(scrollText);
        		}
        		break;
        	case OPEN:
        		pdir = _sessions[UI];
        		break;
        	case NEW:
        		pdir = _sessions[UI];
        		break;
        	case ABORT:
        		if(last_session == BUILD )
        			pdir = _sessions[BUILD];
        		else if (last_session ==  COMMAND)
        			pdir = _sessions[COMMAND];
        		break;
        	case METAMODEL:
        		pdir = _sessions[BUILD];
        		break;
        	case NEXT:
        		pdir = _sessions[COMMAND];
        		break;
        	case RELOAD:
        		pdir = _sessions[UI];
        		break;
        	case UNSAT:
        		pdir = _sessions[COMMAND];
        		break;
        	case SAVE_FILE:
                    pdir = _sessions[UI];
        	    break;
        	case SAVE_FILE_AS:
        		pdir = _sessions[UI];
        		break;
        	case SAVE_COPY_AS:
        		pdir = _sessions[UI];
        		break;	
        	case OPEN_READ_ONLY:
        		pdir = _sessions[UI];
        		break;
        	case USER_GUIDE:
        		pdir = _sessions[UI];
        		break;
        	case VISUALIZATION:
        		pdir = _sessions[COMMAND];
        		break;
        	case HIDE_EDITOR:
        		pdir = _sessions[UI];
                        break;
        	case HIDE_OUTPUT:
        		pdir = _sessions[UI]; 
                        break;
        	case HIDE_TABLES:
        		pdir = _sessions[UI];
                        break;
        	case SHOW_EDITOR:
        		pdir = _sessions[UI];
                        break;
        	case SHOW_OUTPUT:
        		pdir = _sessions[UI];
                        break;
        	case SHOW_TABLES:
        		pdir = _sessions[UI];
        		break;
        }
        
        if (!((pdir != null) && pdir.exists() && pdir.isDirectory())) {
            // J, take a look at this!
            // Dbg.warn("Missing logging directory: " + ((pdir == null)?"null":pdir.getAbsolutePath()));
            return;
        }
        
        File dir;
        dir = new File(pdir, _sessionPrefix[session] + "-" + System.currentTimeMillis());

        dir.delete();
        dir.mkdir();
        if (!(dir.exists() && dir.isDirectory())) {
            Dbg.warn("Could not create logging directory: " + dir.getAbsolutePath());
            return;
        }
        _sessions[session] = dir.getAbsoluteFile();
        
        last_session = session;
    }
    
    private static int logSessionNum = 0;
    
    public static void logEndSession(int session) {
        if (!isLoggingOn()) return;
        setupLogDir();
        
        File dir = _sessions[session];
        if (!((dir != null) && dir.exists() && dir.isDirectory())) {
            // J, take a look at this!
            // Dbg.warn("Missing logging directory: " + ((dir == null)?"null":dir.getAbsolutePath()));
            return;
        }
        
        saveLogFile(dir, "END_TIME", System.currentTimeMillis() + "\n");
        
        if(ifAutoLog){
        	if((session < autoLogLevel) && (logSessionNum > 0))
        		postLog();
        	else if (session == autoLogLevel){
        		logSessionNum++;

        		final int interval = Params.glob.getIntParam("DEVEL","loginterval");
        		
        		if(interval > 0 && (logSessionNum % interval == 0)){
        			postLog();
        		}
        	}
        }
        
        if(0 == session){
        	deleteLogDirs(true);
        }
    }
    
    public static void logFile(int session, String fileName) {
        if (!isLoggingOn()) return;
        setupLogDir();
        
        File dir = _sessions[session];
        if (!((dir != null) && dir.exists() && dir.isDirectory())) {
            // J, take a look at this!
            // Dbg.warn("Missing logging directory: " + ((dir == null)?"null":dir.getAbsolutePath()));
            return;
        }
        
        saveLogFile(dir, "FILE_NAME", fileName + "\n");
    }
    
    public static void logViz(int session, int mode) {
        if (!isLoggingOn()) return;
        setupLogDir();
        
        File dir = _sessions[session];

        if (!((dir != null) && dir.exists() && dir.isDirectory())) {
            // J, take a look at this!
            // Dbg.warn("Missing logging directory: " + ((dir == null)?"null":dir.getAbsolutePath()));
            return;
        }
        
        switch(mode){
        case VIZ_MODE:
            saveLogFile(dir, "VIZUALIZATION_MODE", "VIZ_MODE" + "\n");
        	break;
        case TREE_MODE:
            saveLogFile(dir, "VIZUALIZATION_MODE", "TREE_MODE" + "\n");
        	break;
        case TEXT_MODE:
            saveLogFile(dir, "VIZUALIZATION_MODE", "TEXT_MODE" + "\n");
        	break;
        case XML_MODE:
            saveLogFile(dir, "VIZUALIZATION_MODE", "XML_MODE" + "\n");
        	break;
        case XML_ABBREV_MODE:
            saveLogFile(dir, "VIZUALIZATION_MODE", "XML_ABBREV_MODE" + "\n");
        	break;
        case TABLE_MODE:
            saveLogFile(dir, "VIZUALIZATION_MODE", "TABLE_MODE" + "\n");
        	break;
        case DOT_MODE:
            saveLogFile(dir, "VIZUALIZATION_MODE", "DOT_MODE" + "\n");
        	break;
        case CONSOLE_MODE:
            saveLogFile(dir, "VIZUALIZATION_MODE", "CONSOLE_MODE" + "\n");
        	break;
        }
        
    }

    private static void createDiff(){
    	File pdir = _logDir;
    	File file= null;
    	File file2= null;
    	File[]uiDir;
    	File[]buildDirs;
    	int i = 0;
    	uiDir = pdir.listFiles();
    	buildDirs = uiDir[0].listFiles();
    	
    	for( i = 0; i<buildDirs.length;i++)
    	{
    		int found;
    		found = buildDirs[i].getName().indexOf("build");
    		if(found!=-1)
    		{
    			boolean exists = (new File(buildDirs[i].getPath()+"\\main.als")).exists();
    			if(exists)
    			{
    				if(file==null)
    				{
    					file = new File(buildDirs[i].getPath()+"\\main.als");
    				}
    				else if(file2==null)	
    				{
    					String diff;
    					file2 = new File(buildDirs[i].getPath()+"\\main.als");
    					diff = getDiff(getContent(file.getPath()),getContent(file2.getPath()));
    					saveLogFile(buildDirs[i],"main.als.diff", diff);
    					file = file2;
    				}
    				else
    				{
    					String diff;
    					file2 = new File(buildDirs[i].getPath()+"\\main.als");
    					diff = getDiff(getContent(file.getPath()),getContent(file2.getPath()));
    					saveLogFile(buildDirs[i],"main.als.diff", diff);
    					System.gc();
    					//file.delete();
    					if(file.delete())
    						Dbg.info("file was deleted");
    					else
    						Dbg.info("file wasn't deleted");
    					file = file2;
    				}		
    			}
    		}
    	}
    	
    	//Remember to delete last file
    	if(i>1)
    	{
    		System.gc();	
    		file.delete();
    	
    	}
    	
    }
    
    private static void postLog() {
        if (!isLoggingOn())
	    return;
	
    createDiff();	   
	File pdir = _logDir.getParentFile();
	
	//since logs are being posted, restart diff holders
	buildNum = 0;
    	File log = new File(pdir, LOG_DIR_PREFIX+System.currentTimeMillis()+".zip");
	try {
	    packLogs(log);
			
	    deleteLogDirs(false);
	    logSessionNum = 0;
			
	} catch (Exception e) {
	    //handle exception 
	    Dbg.warn("Failed to pack log file!");
	}
	
        boolean agreed = true;
        
        if(ifAskBeforePost()){
	    try {
		agreed = LogPostAsker
		    .askToPost("Please support the further development of Alloy.\n"
			       + "You may post a log of your analyses (via HTTP).\n"
			       + "The log file ("+log.getAbsolutePath()+") includes your \n"
			       + "Alloy source and configuration files but no other information.\n");
	    } catch (RuntimeException e_) {
		Dbg.warn("Log post failed:\n" + Util.str(e_));
	    }
        } else
	    agreed = true;
        
        if(!agreed){
	    log.deleteOnExit();
	    log.delete();
	    return;
	}
        
		
	String[] dirList = pdir.list(new AlloyLogWildCardFilter());
	int bytesIn = 0;
	//loop through dirList, and zip the files
	for (int i = 0; i < dirList.length; i++) {
	    File f = new File(pdir, dirList[i]);
			
	    if(!f.isFile())
		continue;
			
	    if(LogPoster.postLog(f.getAbsolutePath())){
		f.deleteOnExit();
		f.delete();
	    } else
		Dbg.warn("Failed to post your log.  Please support further development of Alloy by submitting "+f.getAbsolutePath()+" to "+"http://gemini.cs.uiuc.edu/upload.html!");
	}
    }
    
    private static void logCFG(File dir)
    {
    	if(cfgNum == 0){
    	    cfg1 = new String();
    	    cfg2 = new String();
    	} else {
            cfg1 = cfg2;
    	}
    	
    	cfgNum++;
    	
    	File cfg = new File(dir, "alloy.cfg");
    	try {
    		if(cfgNum<2) Params.glob.save(new FileOutputStream(cfg));
    		ByteArrayOutputStream tmp = new ByteArrayOutputStream();
    		Params.glob.save(tmp);
    		//Dbg.info(tmp.toString());
			cfg2 = tmp.toString();//getContent(cfg.getAbsolutePath());
			if(cfgNum > 1){
				saveLogFile(dir, "alloy.cfg" + ".diff", getDiff(cfg1, cfg2));
				cfg.delete();
			}
			//Dbg.info(getContent(cfg.getAbsolutePath()));
		} catch (FileNotFoundException e) {
			Dbg.info("Fail to save alloy.cfg " + e);
		}
    }
    
    /** save the models prepared for analysis */
    public static void logBuild(Specification spec_, String specString_) {
    	int modelCounter = 0;
    	String filename = null;
    	
    	if (!isLoggingOn()) return;
    	
    	if(buildNum == 0){
    	    buildF1 = new String();
    	    buildF2 = new String();
    	    modelMap = new HashMap();
    	    //specF1 = new String();
    	    //specF2 = new String();
    	    
    	}
    	
    	if(specNum == 0){
    	    specF1 = new String();
    	    specF2 = new String();
    	}
    	
        if (!isLoggingOn()) return;
        setupLogDir();
        
        File buildDir = _sessions[BUILD];
        if (!((buildDir != null) && buildDir.exists() && buildDir.isDirectory())) {
            // J, take a look at this!
            // Dbg.warn("Missing logging directory: " + ((buildDir == null)?"null":buildDir.getAbsolutePath()));
            return;
        }
        
        // save the files
        boolean first = true;
        java.util.Set savedFiles = new java.util.HashSet();
        
        if (spec_ != null) {
        
        for (Iterator moduleIter = spec_.getModules().getModuleIter();
             moduleIter.hasNext(); ) {
            alloy.ast.Module module = (alloy.ast.Module)moduleIter.next();
            alloy.ast.ModuleId moduleId = (alloy.ast.ModuleId)module.getName().getId();
            filename = moduleId.getLocation().getFileName();
            // skip the "alloy/lang" modules
            if (first) {
                // save the current module in a file with special name
                if(specString_ != null){
                    saveLogFile(buildDir, "main.als", "// prepareSpecString\n" + specString_);
                    specF2 =  "// prepareSpecString\n" + specString_;
                    
                    specNum++;
                    //compute and place diffs in directory
                    if(specNum > 1){
                    	
                    	//File j = new File(buildDir.getAbsolutePath() + "\\main.als");
                        //saveLogFile(buildDir,"main.als" +".diff", getDiff(specF1, specF2));   
                        //j.delete();
                    }
                    
                    specF1 = specF2;
                    
                   // Dbg.info(filename);
                }
                else {
                    if (filename == null || filename.equals("[not from file]")) continue;

                    buildNum++;
                    
                    saveLogFile(buildDir, "main.als", getContent(filename));
                    
                    buildF2 = getContent(filename);
                    
                    //compute and place diffs in directory
                    if(buildNum > 1){   
                    	
                    	//File j = new File(buildDir.getAbsolutePath() + "\\main.als");
                        //saveLogFile(buildDir, "main.als" + ".diff", getDiff(buildF1, buildF2));
                        //j.delete();
                    }
                    buildF1 = buildF2;
                }
                first = false;
            } else if (savedFiles.add(filename)) {
                if (filename == null || filename.equals("[not from file]")) continue;

                // copy the other modules
                saveLogFile(buildDir, filename, getContent(filename));
                
                if(modelNum == 0){
                	modelMap.put(new Integer(modelCounter), getContent(filename));  
                	modelCounter++;
                }
                
                modelNum++;
                
                //compute and place diffs in directory
                if(modelNum> 1){   

                	if(modelMap.containsKey(new Integer(modelCounter))){
                		
                		//saveLogFile(buildDir, filename +".diff", getDiff((String)modelMap.get(new Integer(modelCounter)),
                		//		getContent(filename)));
                		
                		modelMap.put(new Integer(modelCounter), getContent(filename));
                	}
                	      	
                }
          
            }
        }
        
    }else {
    	
    	
    	if(specString_ == null){
    		
    		saveLogFile(buildDir,"BUILD_FAILED",String.valueOf(System.currentTimeMillis()));
    		
    	}
    	
        saveLogFile(buildDir, "main.als", "// prepareSpecString\n" + specString_);
        specF2 =  "// prepareSpecString\n" + specString_;
        
        specNum++;
        //compute and place diffs in directory
        if(specNum > 1){
        	
        	//File j = new File(buildDir.getAbsolutePath() + "\\main.als");
            //saveLogFile(buildDir,"main.als" +".diff", getDiff(specF1, specF2));   
            //j.delete();
        }
        
        specF1 = specF2;
    	
    }
        
        //compute and place diffs in directory

        
        logCFG(buildDir);
    }
    
    public static void logCommand(Command command_) {
        if (!isLoggingOn()) return;
        setupLogDir();
        
        File dir = _sessions[COMMAND];
        if (!((dir != null) && dir.exists() && dir.isDirectory())) {
            // J, take a look at this!
            // Dbg.warn("Missing logging directory: " + ((dir == null)?"null":dir.getAbsolutePath()));
            return;
        }

        // we don't want to call this again, because it will make a new directory
        //if (!initLogDir()) return;
        saveLogFile(dir, "command", command_.nodeString() + "\n");
        
        logCFG(dir);
    }
    
    private static String getDiff(String s1, String s2){
    	return s2;
    }
    /* Darko commented this out because of problems with non-determinism. */
    /*
        Diff diff; //= new Diff(f1,f2);
        Diff.change script;// = diff.diff_2(false);
    	
        int i;
        
        ArrayList listOne = new ArrayList();
        
        ArrayList listTwo = new ArrayList();   
        
        DiffPrint.Base p;
        
        StringWriter wtr;
        
        String [] arrayOne;
        
        String [] arrayTwo;
        
        StringBuffer bBuff;
        
        StringBuffer cBuff;
    	
    	bBuff = new StringBuffer(s1);
    	
    	cBuff = new StringBuffer(s2);
    	
    	
    	for( i = 0; bBuff.length() > 0; i++){
    		int j = bBuff.indexOf("\n");
    		
    		//b[i] = bBuff.substring(0, j);
    		
    		
    		if(j<0){
    			listOne.add(i,bBuff.toString());
    			i++;
    			break;
    			
    		}
    		listOne.add(i,bBuff.substring(0, j));
    		
    		
    		bBuff.delete(0, j+1);
    		
    	}
    	
    	arrayOne = new String [i];
    	
    	for(int k = 0; k< i; k++){
    		
    		arrayOne[k] = (String)listOne.get(k);
    	}
    	
    	
    	for( i = 0; cBuff.length() > 0; i++){
    		int j = cBuff.indexOf("\n");
    		if(j<0){
    			listTwo.add(i, cBuff.toString());
    			i++;
    			break;
    		}
    		listTwo.add(i,cBuff.substring(0, j));	
    		cBuff.delete(0, j+1);
    	}
    	
    	
    	arrayTwo = new String [i];
    	
    	for( int k = 0; k < i; k++)
    		arrayTwo[k] = (String)listTwo.get(k);
    	
    	
    	diff = new Diff(arrayOne,arrayTwo);
        script = diff.diff_2(false);
        
        p = new DiffPrint.EdPrint(arrayOne,arrayTwo);
        
        wtr = new StringWriter();
        
        p.setOutput(wtr);
        
        p.print_script(script);
        
        return wtr.toString();
    }
    */
    
    /** save the given content into a log file with the given name. */
    private static void saveLogFile(File dir, String filename_, String content_) {
        File toFile = new File(dir, filename_);
        toFile.getParentFile().mkdirs();
        java.io.PrintStream stream = null;
        try {
            stream =  new java.io.PrintStream(new FileOutputStream(toFile));
        } catch (java.io.IOException e) {
            Dbg.warn("Could not create logging file: " + toFile.getAbsolutePath());
        }
        stream.print(content_);
        stream.close();
    }

    private static boolean deleteDir(File dir)
    {
        if (dir.isDirectory()) {
            String[] children = dir.list();
            for (int i=0; i<children.length; i++) {
                boolean success = deleteDir(new File(dir, children[i]));
                if (!success) {
                    return false;
                }
            }
        }
    
        // The directory is now empty so delete it
        dir.deleteOnExit();
        return dir.delete();
    }
    
	private static void zipDir(File root, File dir2zip, ZipOutputStream zos) {
		try {
			//get a listing of the directory content
			String[] dirList = dir2zip.list();
			byte[] readBuffer = new byte[2156];
			int bytesIn = 0;
			//loop through dirList, and zip the files
			for (int i = 0; i < dirList.length; i++) {
				File f = new File(dir2zip, dirList[i]);
				if (f.isDirectory()) {
					//if the File object is a directory, call this
					//function again to add its content recursively
					File filePath = new File(f.getPath());
					zipDir(root, filePath, zos);
					//loop again
					continue;
				}
				//if we reached here, the File object f was not a directory 
				//create a FileInputStream on top of f
				FileInputStream fis = new FileInputStream(f);
				//create a new zip entry 
				ZipEntry anEntry = new ZipEntry(RelativePath.getRelativePath(root, f));
				//place the zip entry in the ZipOutputStream object
				zos.putNextEntry(anEntry);
				//now write the content of the file to the ZipOutputStream
				while ((bytesIn = fis.read(readBuffer)) != -1) {
					zos.write(readBuffer, 0, bytesIn);
				}
				//close the Stream
				fis.close();
			}
		} catch (Exception e) {
			//handle exception
		}
	}
	
	  private static void addZip(ZipOutputStream destinationZip, ZipFile sourceZip){
	  	Set directorySet = new HashSet();
	  	Set fileSet = new HashSet();
	    
	    int buffersize = 4192;
	    byte buffer[] = new byte[buffersize];
	    
		try {

			// get an enumeration of all entries from the sourceZip
			Enumeration zipFileEntriesEnum = sourceZip.entries();

			ZipEntry currentZipEntry = null;
			while (zipFileEntriesEnum.hasMoreElements()) {

				// get a ZipEntry
				currentZipEntry = (ZipEntry) zipFileEntriesEnum.nextElement();

				// if current entry is manifest then it is skipped
				if (currentZipEntry.getName().equalsIgnoreCase("META-INF/")
						|| currentZipEntry.getName().equalsIgnoreCase(
								"META-INF/MANIFEST.MF"))
					continue;

				// if current entry is a directory that was previously added to
				// the
				// destination ZIP then it is skipped
				if (currentZipEntry.isDirectory()
						&& directorySet.contains(currentZipEntry.getName()))
					continue;

				// otherwise the current entry is added to the final zip file
				try {
					// if the entry is directory then is added to the
					// directorySet
					// NOTE: files entries are not added to this set
					if (currentZipEntry.isDirectory())
						directorySet.add(currentZipEntry.getName());

					// put the entry into the destination ZIP
					destinationZip.putNextEntry(new ZipEntry(currentZipEntry
							.getName()));

					// add the binary data from the entry
					// NOTE: if the entry is a directory there will be no binary
					// data
					// get an input stream from the entry
					InputStream currentEntryStream = sourceZip.getInputStream(currentZipEntry);

					// write data to destinationZip
					int bytesRead = 0;
					while ((bytesRead = currentEntryStream.read(buffer, 0, buffersize)) != -1)
						destinationZip.write(buffer, 0, bytesRead);

					// close the input stream
					currentEntryStream.close();

					// flush the destinationZip in order to be sure that
					// everything is there
					destinationZip.flush();

					// close the new added entry and prepare to read and write
					// another one
					// NOTE: destinationZip.putNextEntry automaticaly closes any
					// previous
					// opened entry
					destinationZip.closeEntry();

				} catch (java.util.zip.ZipException ze) {
					if (!currentZipEntry.isDirectory()) {
						Dbg.warn("WARNING: Duplicate file entry "
								+ currentZipEntry.getName()
								+ " (this file will be discarded)..."
								+ "It happened while adding "
								+ sourceZip.getName() + " !\n");
					}// End if
				}
			}// while(zipFileEntriesEnum.hasMoreElements())
		} catch (java.io.IOException e) {
			Dbg.warn("Error during packing of log file!");
		}
	}// addZip
}

class AlloyLogWildCardFilter implements FilenameFilter {
	public boolean accept(File dir, String name) {
		File tempFile = new File(dir, name);

		if (tempFile.isDirectory()) {
			if(name.startsWith(AlloyLog.LOG_DIR_PREFIX))
				return true;
		} else if(tempFile.isFile()){
			if(name.startsWith(AlloyLog.LOG_DIR_PREFIX) && name.endsWith("zip"))
				return true;
		}
		
		return false;
	}
}

class RelativePath {
	/**
	 * break a path down into individual elements and add to a list.
	 * example : if a path is /a/b/c/d.txt, the breakdown will be [d.txt,c,b,a]
	 * @param f input file
	 * @return a List collection with the individual elements of the path inreverse order
	 */
	private static List getPathList(File f) {
		List l = new ArrayList();
		File r;
		try {
			r = f.getCanonicalFile();
			while(r != null) {
				l.add(r.getName());
				r = r.getParentFile();
			}
		}
		catch (IOException e) {
			e.printStackTrace();
			l = null;
		}
		return l;
	}

	/**
	 * figure out a string representing the relative path of
	 * 'f' with respect to 'r'
	 * @param r home path
	 * @param f path of file
	 */
	private static String matchPathLists(List r,List f) {
		int i;
		int j;
		String s;
		// start at the beginning of the lists
		// iterate while both lists are equal
		s = "";
		i = r.size()-1;
		j = f.size()-1;

		// first eliminate common root
		while((i >= 0)&&(j >= 0)&&(r.get(i).equals(f.get(j)))) {
			i--;
			j--;
		}

		// for each remaining level in the home path, add a ..
		for(;i>=0;i--) {
			s += ".." + File.separator;
		}

		// for each level in the file path, add the path
		for(;j>=1;j--) {
			s += f.get(j) + File.separator;
		}

		// file name
		s += f.get(j);
		return s;
	}

	/**
	 * get relative path of File 'f' with respect to 'home' directory
	 * example : home = /a/b/c
	 *           f    = /a/d/e/x.txt
	 *           s = getRelativePath(home,f) = ../../d/e/x.txt
	 * @param home base path, should be a directory, not a file, or it doesn't
make sense
	 * @param f file to generate path for
	 * @return path from home to f as a string
	 */
	public static String getRelativePath(File home, File f){
		File r;
		List homelist;
		List filelist;
		String s;

		homelist = getPathList(home);
		filelist = getPathList(f);
		s = matchPathLists(homelist,filelist);

		return s;
	}
}
